/*
Copyright 2021 LuJun

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal 
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is furnished 
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { error } from 'cc';
import { js, path } from '../core/utils';
import { variable, event, EVENT_PACK_LOADING, EVENT_INDEXES_LOAD } from '../core';
import { readJsonConfig } from '../core/jsonconfig';

const TAG = 'lcc/index/index-manager.ts';

/**
 * @zh
 * 包索引JSON配置文件
 */
export const PACK_INDEX_JSON_CONFIG = 'index-files.json';

/**
 * @zh
 * 索引管理器<br/>
 * 管理游戏中的公用资源索引
 */
class IndexManager {
    /**
     * @zh
     * 单例对象
     */
    private static _instance: IndexManager = null!;

    /**
     * @zh
     * 获得单例对象
     */
    static get instance() {
        if (IndexManager._instance === null) {
            IndexManager._instance = new IndexManager();
        }
        return IndexManager._instance;
    }

    /**
     * @zh
     * 构造函数
     */
    private constructor() {
        event.on(EVENT_PACK_LOADING, (packName: string) => {
            this.loadIndexJsonConfig(`asset-json://${packName}/${PACK_INDEX_JSON_CONFIG}`);
        });
    }

    /**
     * @zh
     * 索引树
     */
    private _indexTree: any = Object.create(null);

    /**
     * @zh
     * 获得指定索引
     *
     * @param path - 索引路径
     */
    getIndex<T = any>(path: string) {
        if (path) {
            return js.getMapValue<T>(this._indexTree, path, '/');
        }
    }

    /**
     * @zh
     * 更新索引
     *
     * @param value 索引值 string/number/object/array
     * @param path1 可选，更新路径
     */
    updateIndex(value: any, path1: string = null) {
        let indexes: any = null;
        if (path1) {
            indexes = Object.create(null);
            let root = indexes;
            let parts = path1.split('/');
            for (let i = 0; i < parts.length - 1; i++) {
                let node = Object.create(null);
                root[parts[i]] = node;
                root = node;
            }
            root[parts[parts.length - 1]] = value;
        } else {
            indexes = value;
        }

        let indexesType = typeof indexes;
        if (indexesType !== 'object') {
            error(TAG, `updateIndex type '${indexesType}' is not object`);
            return;
        }

        js.mergeMap(this._indexTree, indexes, {
            source: 'IndexManager.updateIndex()',
            callback: (path2: string, value: any) => {
                try {
                    let fullPath = path1 ? path1 + '/' + path2 : path2;
                    event.emit(EVENT_INDEXES_LOAD + fullPath, value);
                    event.emit(EVENT_INDEXES_LOAD, fullPath, value);
                } catch (e) {
                    error(TAG, `updateIndex Exception ${e}`);
                }
            }
        });
    }

    /**
     * @zh
     * 加载索引Json文件<br/>
     * 注意：会触发异步加载
     * 用JSON文件保存索引配置
     *
     * @param jsonUrl - JSON文件路径，符合[Request]要求
     */
    async loadIndexJson(jsonUrl: string) {
        let indexes = await readJsonConfig(jsonUrl);
        if (indexes) {
            indexes = variable.formatJSON(indexes, { localpath: path.dirname(jsonUrl) });
            this.updateIndex(indexes);
        }
    }

    /**
     * @zh
     * 加载索引Json配置文件<br/>
     * 注意：会触发异步加载
     * 此JSON文件格式为 {[key:string]: Index JSON文件} 是索引Json文件的集合
     *
     * @param jsonConfigUrl JSON配置文件路径，符合[Request]要求
     */
    async loadIndexJsonConfig(jsonConfigUrl: string) {
        let indexConfig = await readJsonConfig(jsonConfigUrl);
        if (indexConfig) {
            indexConfig = variable.formatJSON(indexConfig, { localpath: path.dirname(jsonConfigUrl) });
            for (let k in indexConfig) {
                await this.loadIndexJson(indexConfig[k]);
            }
        }
    }
}

/**
 * @zh
 * 索引管理器对象
 */
export const indexMgr = IndexManager.instance;
